package com.zhangyh.auto.config;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import java.io.*;
import java.util.*;

public class AutoCreate {
  private static final Logger log = LogManager.getLogger(AutoCreate.class);
  private static Map<String, String> _dataType = new HashMap();
  
  public static void autoCreateMybatisPlusService(String packageName,String configModule, String outputModule) {
    
    String mysqlConfigName = "application.properties";
    String eneityName = "entity";
    
    log.info("-------参数------");
    log.info("包名:" + packageName);
    log.info("配置文件模块:" + configModule);
    log.info("生成文件模块:" + outputModule);
    log.info("数据库配置：" + mysqlConfigName);
    log.info("实体目录名称：" + eneityName);
    
    initConfigDir(configModule, outputModule, packageName, mysqlConfigName, eneityName, true, true);
  }
  
  
  //配置生成路径
  private static void initConfigDir(String configModule, String outputModule, String packageName, String configFileName, String entityDirectoryName, boolean mybatisPlus, boolean mybatisPlusService) {
    //获取当前项目路径
    String mainPath = System.getProperty("user.dir");
    String splitStr = "/";
    //windows  \\\\ 两个斜杠 特殊字符转义
    if (File.separator.toString().equals("\\")) {
      splitStr = "\\\\";
    }
    
    String[] dir = mainPath.split(splitStr);
    
    String ml;
    if (dir.length > 0) {
      ml = dir[dir.length - 1];
      //判断是否是多模块项目
      if (!ml.equals(configModule) && !ml.equals(outputModule)) {
        //多模块项目 生成路径为当前项目路径 + 模块名
        mainPath = mainPath + File.separator + configModule;
      }
    }
    
    
    String configPath = mainPath + File.separator + ".." + File.separator + configModule + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + configFileName;
    String outputPath = mainPath + File.separator + ".." + File.separator + outputModule;
    log.info("configPath => " + configPath);
    log.info("outputPath => " + outputPath);
    
    
    JdbcTemplate jdbcTemplate = getJdbcTemplate(configPath);
    if (jdbcTemplate != null) {
      autoCreateStart(jdbcTemplate, outputPath, packageName, entityDirectoryName, mybatisPlus);
    }
    log.info("-------END------");
  }
  
  private static void autoCreateStart(JdbcTemplate jdbcTemplate, String outputPath, String packageName, String entityDirectoryName, boolean mybatisPlus) {
    String entityPath = outputPath + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + packageName.replace(".", File.separator) + File.separator + entityDirectoryName;
    log.info("entityPath => " + entityPath);
    
  
    List<Map<String, Object>> tables = getTableAll(jdbcTemplate);
    Iterator var11 = tables.iterator();
    while (var11.hasNext()) {
      Map<String, Object> table = (Map) var11.next();
      //获取表名
      String table_name = table.get("Name").toString();
      //首字母大写并且去除下划线_
      String tableName = getTableName(table_name);
      
      //生成entity
      log.info("生成Entity...");
      autoCreateEntity(jdbcTemplate, packageName, entityDirectoryName, mybatisPlus, tableName, table_name, entityPath);
      //生成mapper
      log.info("生成Mapper...");
      autoCreateMapper(outputPath, packageName, "mapper", entityDirectoryName, tableName);
      //生成service
      log.info("生成Service...");
      autoCreateService(outputPath, packageName, "service", entityDirectoryName, tableName);
    }
  }
  
  private static void autoCreateEntity(JdbcTemplate jdbcTemplate, String packageName, String entityDirectoryName, boolean mybatisPlus, String tableName, String table_name,String entityPath) {
    String modelInfo;
    String sql = "SHOW FULL COLUMNS FROM `" + table_name + "` ";
    List maps = jdbcTemplate.queryForList(sql);
    
    //生成头部参数
    modelInfo = getModelTop(packageName + "." + entityDirectoryName, tableName, table_name, mybatisPlus);
    //生成私有属性字段参数
    modelInfo += _getPrivate(maps, mybatisPlus, tableName);
    
    if (!modelInfo.equals("")) {
      //生成toString方法
      modelInfo = modelInfo + "  @Override\n  public String toString(){\n   return JSONUtil.toJsonStr(this);\n  }\n";
      modelInfo = modelInfo + "\n}";
      //保存entity文件
      String filePath = entityPath + File.separator + tableName + ".java";
      saveFile(filePath, modelInfo);
    }
  }
  
  
  private static void autoCreateService(String entityMain, String entityPackage, String serviceDirectoryName, String entityDirectoryName, String tableName) {
    String serviceFile = entityMain + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + entityPackage.replace(".", File.separator) + File.separator + serviceDirectoryName + File.separator + tableName + "Service.java";
    String info = "package {package}.{service};\n" +
      "import {package}.{entity}.{model};\n" +
      "import com.baomidou.mybatisplus.extension.service.IService;\n\n" +
      "public interface {model}Service extends IService<{model}> {\n\n}\n";
    info = info.replace("{package}", entityPackage);
    info = info.replace("{service}", serviceDirectoryName);
    info = info.replace("{entity}", entityDirectoryName);
    info = info.replace("{model}", tableName);
    if (!info.equals("")) {
      File file = new File(serviceFile);
      if (!file.exists()) {
        saveFile(serviceFile, info);
      }
    }
    
    autoCreateServiceImpl(entityMain, entityPackage, serviceDirectoryName, entityDirectoryName, tableName);
  }
  
  private static void autoCreateServiceImpl(String entityMain, String entityPackage, String serviceDirectory, String entityDirectory, String modelName) {
    String implFile = entityMain + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + entityPackage.replace(".", File.separator) + File.separator + serviceDirectory + File.separator + "impl" + File.separator + modelName + "ServiceImpl.java";
    String info = "package {package}.{service}.impl;\n\n" +
      
      "import {package}.{entity}.{model};\nimport {package}.mapper.{model}Mapper;\n" +
      "import {package}.{service}.{model}Service;\n" +
      "import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\n" +
      "import org.springframework.stereotype.Service;\n\n" +
      "@Service\n" +
      "public class {model}ServiceImpl extends ServiceImpl<{model}Mapper, {model}> implements {model}Service {\n\n\n}\n";
    info = info.replace("{package}", entityPackage);
    info = info.replace("{service}", serviceDirectory);
    info = info.replace("{entity}", entityDirectory);
    info = info.replace("{model}", modelName);
    if (!info.equals("")) {
      File file = new File(implFile);
      if (!file.exists()) {
        saveFile(implFile, info);
      }
    }
    
  }
  
  private static void autoCreateMapper(String entityMain, String entityPackage, String mapperDirectory, String entityDirectory, String tableName) {
    String mapperFile = entityMain + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + entityPackage.replace(".", File.separator) + File.separator + mapperDirectory + File.separator + tableName + "Mapper.java";
    String info = "package {package}.{mapper};\n\nimport {package}.{entity}.{model};\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface {model}Mapper extends BaseMapper<{model}> {\n\n}\n";
    info = info.replace("{package}", entityPackage);
    info = info.replace("{mapper}", mapperDirectory);
    info = info.replace("{entity}", entityDirectory);
    info = info.replace("{model}", tableName);
    if (!info.equals("")) {
      File file = new File(mapperFile);
      //如果文件不存在 保存文件
      if (!file.exists()) {
        saveFile(mapperFile, info);
      }
    }
    //创建 xml文件
    autoCreateResourcesMapper(entityMain, entityPackage, mapperDirectory, tableName);
  }
  
  private static void autoCreateResourcesMapper(String entityMain, String entityPackage, String mapperDirectory, String tableName) {
    String resMapper = entityMain + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + mapperDirectory + File.separator + tableName + "Mapper.xml";
    String info = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"{package}.{mapper}.{model}Mapper\">\n\n</mapper>\n";
    info = info.replace("{package}", entityPackage);
    info = info.replace("{mapper}", mapperDirectory);
    info = info.replace("{model}", tableName);
    if (!info.equals("")) {
      File file = new File(resMapper);
      if (!file.exists()) {
        saveFile(resMapper, info);
      }
    }
  }
  
  public static boolean saveFile(String filePath, String fileInfo) {
    return save(filePath, fileInfo, false);
  }
  
  private static boolean save(String filePath, String fileInfo, boolean append) {
    if ("".equals(filePath)) {
      return false;
    } else {
      File file = new File(filePath);
      
      try {
        if (!append && file.exists()) {
          file.delete();
        }
        
        File fileParent = file.getParentFile();
        if (!fileParent.exists()) {
          fileParent.mkdirs();
        }
        
        if (!file.exists()) {
          file.createNewFile();
        }
        
        OutputStreamWriter write;
        BufferedWriter writer;
        if (append) {
          write = new OutputStreamWriter(new FileOutputStream(file, true), "utf-8");
          writer = new BufferedWriter(write);
          writer.write(fileInfo);
          writer.close();
        } else {
          write = new OutputStreamWriter(new FileOutputStream(file), "utf-8");
          writer = new BufferedWriter(write);
          writer.write(fileInfo);
          writer.close();
        }
        
        return true;
      } catch (IOException var7) {
        var7.printStackTrace();
        return false;
      }
    }
  }
  
  private static String _getPrivate(List<Map<String, Object>> _columns, boolean mybatisPlus, String modelName) {
    String _return = "  \n";
    String tpl = "  private %s %s;\n\n";
    String _columnName;
    String _columnType;
    String info;
    String comment;
    
    Iterator<Map<String, Object>> iterator = _columns.iterator();
    while (iterator.hasNext()) {
      Map<String, Object> column = iterator.next();
      
      String _columnField = column.get("Field").toString();
      
      //获取类型 字段名
      _columnName = getColumnName(_columnField);
      _columnType = _getTypeName(column.get("Type").toString());
      
      info = "";
      if (mybatisPlus && column.get("Key").toString().equals("PRI")) {
        if (column.get("Extra").toString().equals("auto_increment")) {
          info += "  @TableId(value = \"id\", type = IdType.AUTO)\n";
        } else {
          info += "  @TableId(value = \"id\", type = IdType.INPUT)\n";
        }
      }
      
      
      comment = "";
      if (!"".equals(column.get("Comment").toString())) {
        comment = "    /**\n   * " + column.get("Comment").toString() + "\n   */\n";
      }
      
      _return += comment + info + String.format(tpl, _columnType, _columnName);
    }
    
    return _return;
  }
  
  private static String _getTypeName(String _type) {
    String _return = "String";
    if (_dataType.isEmpty()) {
      _dataType.put("varchar", "String");
      _dataType.put("char", "String");
      _dataType.put("text", "String");
      _dataType.put("int", "Integer");
      _dataType.put("tinyint", "Integer");
      _dataType.put("smallint", "Integer");
      _dataType.put("mediumint", "Integer");
      _dataType.put("bit", "Integer");
      _dataType.put("float", "Float");
      _dataType.put("bigint", "Long");
      _dataType.put("double", "Double");
      _dataType.put("decimal", "Double");
      _dataType.put("date", "Date");
      _dataType.put("time", "Date");
      _dataType.put("timestamp", "Long");
      _dataType.put("datetime", "Date");
    }
    
    String dataType = _type;
    //如果有括号 例如 varchar(255) 截取varchar 作为dataType 作为key获取value 作为返回值
    if (_type.indexOf("(") > 0) {
      dataType = _type.split("\\(")[0];
    }
    
    if (_dataType.containsKey(dataType)) {
      _return = _dataType.get(dataType);
    }
    
    return _return;
  }
  
  private static String getModelTop(String _package, String _model, String _tablename, boolean mybatisPlus) {
    //导包
    String _return = "package " + _package + ";\n\nimport java.io.Serializable;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n";
    if (mybatisPlus) {
      _return += "import com.baomidou.mybatisplus.annotation.TableName;\n" +
        "import com.baomidou.mybatisplus.annotation.IdType;\n" +
        "import com.baomidou.mybatisplus.annotation.TableId;\n" +
        "import cn.hutool.json.JSONUtil;\n\n";
      
      _return += "@Data\n@Accessors(chain = true)\n";
      _return += "@TableName(\"" + _tablename + "\")\n";
      _return += "public class " + _model + " implements Serializable {\n";
      _return += "private static final long serialVersionUID = 1L;\n";
    }
    return _return;
  }
  
  private static String getColumnName(String _tablename) {
    String _return = "";
    String[] _names = _tablename.split("_");
    
    for (int i = 0; i < _names.length; ++i) {
      if (i == 0) {
        _return = _names[i];
      } else {
        String _name = _names[i].toLowerCase();
        if (!_name.equals("")) {
          _name = _name.substring(0, 1).toUpperCase() + _name.substring(1);
          _return += _name;
        }
      }
    }
    
    return _return;
  }
  
  private static List<Map<String, Object>> getTableAll(JdbcTemplate jdbcTemplate) {
    String sql = "SHOW TABLE STATUS";
    List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
    return maps;
  }
  
  private static String getTableName(String _tablename) {
    String _return = "";
    
    String[] _names = _tablename.split("_");
    
    for (int i = 0; i < _names.length; ++i) {
      String _name = _names[i].toLowerCase();
      //如果 ——name 不为空
      if (!_name.equals("")) {
        //首字母大写
        _name = _name.substring(0, 1).toUpperCase() + _name.substring(1);
        _return += _name;
      }
    }
    return _return;
  }
  
  private static JdbcTemplate getJdbcTemplate(String configPath) {
    try {
      if ((new File(configPath)).exists()) {
        Properties properties = new Properties();
        properties.load(new FileInputStream(configPath));
        String url = "jdbc:mysql://${mysql.ip}:${mysql.port}/${mysql.database}?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true";
        url = url.replace("${mysql.ip}", properties.getProperty("mysql.ip"));
        url = url.replace("${mysql.port}", properties.getProperty("mysql.port"));
        url = url.replace("${mysql.database}", properties.getProperty("mysql.database"));
        String username = properties.getProperty("mysql.username");
        String password = properties.getProperty("mysql.password");
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        
        return jdbcTemplate;
      }
      log.info("error => 找不到数据库配置文件{}", configPath);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
}
